return {
    "saghen/blink.cmp",
    dependencies = {
        "pmizio/typescript-tools.nvim",
        "rafamadriz/friendly-snippets",
        "nvim-tree/nvim-web-devicons",
        "onsails/lspkind.nvim",
        "mikavilpas/blink-ripgrep.nvim",
    },
    event = { "InsertEnter", "CmdlineEnter", "BufReadPost", "BufNewFile" },
    version = "1.*",
    opts = {
        keymap = {
            ["<C-h>"] = { "show", "show_documentation", "hide_documentation" },
            ["<Tab>"] = {
                function(cmp)
                    if cmp.snippet_active() then
                        return cmp.accept()
                    else
                        return cmp.select_and_accept()
                    end
                end,
                "snippet_forward",
                "fallback",
            },
            ["<c-g>"] = {
                function()
                    -- invoke manually, requires blink >v0.8.0
                    require("blink-cmp").show { providers = { "ripgrep" } }
                end,
            },
        },
        appearance = {
            nerd_font_variant = "normal",
        },
        sources = {
            -- , "ripgrep"
            default = { "buffer", "path", "omni", "lsp", "snippets", "ripgrep" },
            providers = {
                ripgrep = {
                    score_offset = 600,
                    module = "blink-ripgrep",
                    name = "ripgrep",
                    opts = {
                        prefix_min_len = 3,
                        context_size = 3,
                        max_filesize = "3M",
                        project_root_marker = ".git",
                        project_root_fallback = true,
                        search_casing = "--smart-case",
                        additional_rg_options = {},
                        fallback_to_regex_highlighting = true,
                        ignore_paths = {},
                        additional_paths = {},
                        toggles = {
                            on_off = nil,
                        },
                        future_features = {
                            backend = {
                                use = "ripgrep",
                            },
                        },
                        debug = false,
                    },
                    transform_items = function(_, items)
                        for _, item in ipairs(items) do
                            -- example: append a description to easily distinguish rg results
                            item.labelDetails = {
                                description = "(rg)",
                            }
                        end
                        return items
                    end,
                },
                path = {
                    name = "path",
                    score_offset = 500,
                    opts = {
                        get_cwd = function(_)
                            return vim.fn.getcwd()
                        end,
                    },
                },
                lsp = {
                    name = "lsp",
                    score_offset = 800,
                    transform_items = function(_, items)
                        return vim.tbl_filter(function(item)
                            return item.kind ~= require("blink.cmp.types").CompletionItemKind.Text
                        end, items)
                    end,
                    fallbacks = {},
                },
                snippets = {
                    name = "snippets",
                    score_offset = 900,
                    should_show_items = function(ctx)
                        return ctx.trigger.initial_kind ~= "trigger_character"
                    end,
                    fallbacks = {},
                    opts = {
                        search_paths = { vim.fn.stdpath "config" .. "/snippets" },
                    },
                },
                cmdline = {
                    enabled = function()
                        return vim.fn.getcmdtype() ~= ":" or not vim.fn.getcmdline():match "^[%%0-9,'<>%-]*!"
                    end,
                },
                buffer = {
                    name = "buffer",
                    score_offset = 700,
                },
            },
        },
        fuzzy = {
            sorts = {
                -- "exact",
                "score",
                "sort_text",
            },
            -- 安装后取消下面配置
            -- https://cmp.saghen.dev/configuration/fuzzy.html#prebuilt-binaries-default-on-a-release-tag
            -- implementation = "lua",
            implementation = "prefer_rust_with_warning",
        },
        completion = {
            accept = { auto_brackets = { enabled = true } },
            list = { selection = { preselect = true, auto_insert = true } },
            trigger = {
                show_on_keyword = true,
            },
            menu = {
                auto_show = true,
                border = "rounded",
                max_height = 20,
                draw = {
                    columns = { { "label", "label_description", gap = 1 }, { "kind_icon", "kind", "source_name" } },
                    treesitter = { "lsp" },
                    components = {
                        kind_icon = {
                            ellipsis = false,
                            text = function(ctx)
                                local icon = ctx.kind_icon
                                if icon then
                                -- Do nothing
                                elseif vim.tbl_contains({ "Path" }, ctx.source_name) then
                                    local dev_icon, _ = require("nvim-web-devicons").get_icon(ctx.label)
                                    if dev_icon then
                                        icon = dev_icon
                                    end
                                else
                                    icon = require("lspkind").symbolic(ctx.kind, { mode = "symbol" })
                                end
                                return icon .. ctx.icon_gap
                            end,
                            highlight = function(ctx)
                                local hl = ctx.kind_hl
                                if hl then
                                elseif vim.tbl_contains({ "Path" }, ctx.source_name) then
                                    local dev_icon, dev_hl = require("nvim-web-devicons").get_icon(ctx.label)
                                    if dev_icon then
                                        hl = dev_hl
                                    end
                                end
                                return hl
                            end,
                        },
                    },
                },
                documentation = { auto_show = true, auto_show_delay_ms = 500 },
                trigger = {
                    show_on_keyword = true,
                },
            },
            documentation = {
                auto_show = true,
                auto_show_delay_ms = 200,
                window = {
                    min_width = 10,
                    max_width = 120,
                    max_height = 20,
                    border = "rounded",
                    winblend = 0,
                    winhighlight = "Normal:BlinkCmpDoc,FloatBorder:BlinkCmpDocBorder,EndOfBuffer:BlinkCmpDoc",
                    scrollbar = true,
                    direction_priority = {
                        menu_north = { "e", "w", "n", "s" },
                        menu_south = { "e", "w", "s", "n" },
                    },
                },
            },
            ghost_text = {
                enabled = true,
                show_with_selection = true,
                show_without_selection = false,
                show_with_menu = true,
                show_without_menu = true,
            },
        },

        signature = {
            enabled = true,
            window = {
                min_width = 1,
                max_width = 100,
                max_height = 10,
                border = "single", -- Defaults to `vim.o.winborder` on nvim 0.11+ or 'padded' when not defined/<=0.10
                winblend = 0,
                winhighlight = "Normal:BlinkCmpSignatureHelp,FloatBorder:BlinkCmpSignatureHelpBorder",
                scrollbar = false, -- Note that the gutter will be disabled when border ~= 'none'
                direction_priority = { "n" },
                treesitter_highlighting = true,
                show_documentation = true,
            },
        },

        cmdline = {
            completion = {
                menu = {
                    auto_show = true,
                },
            },
            keymap = {
                preset = "none",
                ["<Tab>"] = { "show", "accept" },
                ["<C-k>"] = { "select_prev", "fallback" },
                ["<C-j>"] = { "select_next", "fallback" },
            },
        },
    },
    opts_extend = { "sources.default" },
    config = function(_, opts)
        require("blink.cmp").setup(opts)
    end,
}
